use axum_app_common::{
    database::{self, RawSqlQueryBuilder},
    error::{self, is_duplicate_exception, BizError},
    model::{BizRequest, BizResponse, PageRequest, PageResult},
    my_date_format, service_store,
    validator::RequestValidatorField as _,
    BizHandler, BizHandlerResult, ServiceStore,
};
use chrono::{DateTime, Utc};
use sea_orm::{FromQueryResult, PaginatorTrait as _};
use serde::{Deserialize, Serialize};
use serde_json::json;
use validator::Validate;

use crate::{model::resource::ResourceRoleBindParams, service::resource_svc::ResourceSvc};

pub struct ResourceHandler;

impl BizHandler for ResourceHandler {
    fn service_map() -> ServiceStore {
        service_store! {
            ("resource.create","创建资源") => create_resource;
            ("resource.role.unbind","资源信息绑定角色关系") => role_unbind;
            ("resource.role.bind","资源信息绑定角色关系") => role_bind;
            ("resource.query.page","分页查询资源信息") => query_page;
            ("resource.role.rel.page","分页查询资源与角色绑定信息") => query_role_rel_page
        }
    }
}

#[derive(Debug, Deserialize, Validate)]
struct ResourceQueryParams {
    res_name: Option<String>,
    role_not: Option<String>,
}

#[derive(Debug, FromQueryResult, Serialize)]
pub struct ResourceItem {
    pub id: i64,
    pub delete_flag: i64,
    pub res_code: String,
    pub res_name: String,
    pub res_description: String,
    pub res_type: String,
    #[serde(with = "my_date_format")]
    pub create_time: DateTime<Utc>,
    #[serde(with = "my_date_format")]
    pub update_time: DateTime<Utc>,
}

#[derive(Debug, Deserialize, Validate)]
struct ResourceCreate {
    pub res_code: String,
    pub res_name: String,
    pub res_description: Option<String>,
    pub res_type: String,
}
/// 创建资源
pub async fn create_resource(request: BizRequest) -> BizHandlerResult {
    let state = request.get_state().unwrap();
    let ResourceCreate {
        res_type,
        res_code,
        res_name,
        res_description,
    } = request.get_biz_model::<ResourceCreate>().unwrap();
    let ctx = request.get_context();

    let svc = ResourceSvc::new(&state.db_conn, ctx);

    let result = svc
        .create_resource(&res_type, &res_code, &res_name, res_description.as_deref())
        .await;

    match result {
        Ok(id) => Ok(BizResponse::success(json!({"id": id}))),
        Err(err) => {
            let err_msg = err.to_string();

            let biz_err = err.downcast::<BizError>();

            if biz_err.is_err() {
                return Ok(BizResponse::fail_with_biz_error(
                    BizError::Unknown,
                    Some(&err_msg),
                ));
            }

            let biz_err = biz_err.unwrap();
            let err_msg = match biz_err {
                BizError::DbDuplicateErr(_) => "数据已存在".to_string(),
                _ => err_msg,
            };
            Ok(BizResponse::fail_with_biz_error(biz_err, Some(&err_msg)))
        }
    }
}

/// 资源信息绑定角色关系解除
pub async fn role_unbind(request: BizRequest) -> BizHandlerResult {
    let state = request.get_state().unwrap();
    let ctx = request.get_context();
    let params = request.get_biz_model::<ResourceRoleBindParams>()?;

    let svc = ResourceSvc::new(&state.db_conn, ctx);

    let res_codes: Vec<&str> = params.res_codes.iter().map(|s| s.as_str()).collect();
    let rows_affected = svc.unbind_role(&params.role_code, res_codes).await?;

    Ok(BizResponse::success(json!({"rows_affected":rows_affected})))
}

/// 资源信息绑定角色关系
pub async fn role_bind(request: BizRequest) -> BizHandlerResult {
    let state = request.get_state().unwrap();
    let ctx = request.get_context();
    let params = request.get_biz_model::<ResourceRoleBindParams>()?;

    let svc = ResourceSvc::new(&state.db_conn, ctx);

    let res_codes: Vec<&str> = params.res_codes.iter().map(|s| s.as_str()).collect();
    let _res = svc.bind_role(&params.role_code, res_codes).await?;

    Ok(BizResponse::success(json!({})))
}

/// 分页查询角色信息
pub async fn query_page(request: BizRequest) -> BizHandlerResult {
    let _ = request.get_context();
    let state = request.get_state().unwrap();
    let db = &state.db_conn;

    let biz_content = request.get_biz_content();

    let params = PageRequest::<ResourceQueryParams>::try_from(biz_content)?;

    let Some(query_params) = params.params else {
        return Err(BizError::InvalidParams(None).into());
    };

    let page_num = params.page_num as u32;
    let page_size = params.page_size as u32;
    let mut sql_builder = RawSqlQueryBuilder::new();
    let base_sql = "
    select 
        res.id, 
        res.delete_flag, 
        res.create_time, 
        res.update_time, 
        res.res_code, 
        res.res_name, 
        res.res_description, 
        res.res_type 
    from sys_resource res   
        "
    .to_string();
    if let Some(role_not) = query_params.role_not {
        sql_builder
            .push(
                "and not exists(
                select rel.id from rbac_role_resource_rel rel ",
            )
            .push_bind(
                "where rel.res_code=res.res_code and rel.role_code = ",
                role_not.into(),
            )
            .push(")");
    }
    if let Some(res_name) = query_params.res_name {
        sql_builder.push_bind("and res.res_name like ", format!("{res_name}%").into());
    }

    let sql_and_vals = sql_builder.build(&base_sql, Some("res.res_name asc"));

    let stmt = database::from_string_values_tuple(sql_and_vals);
    let selector = ResourceItem::find_by_statement(stmt);

    let pager = selector.paginate(db, page_size as u64);
    let page_list = pager.fetch_page((page_num - 1) as u64).await.unwrap();
    let total = pager.num_items().await.unwrap();

    let data = PageResult::new(page_num as u64, page_size as u64, total as u64, page_list);
    Ok(BizResponse::success(data.into()))
}

#[derive(Debug, Deserialize, Validate)]
struct ResourceRoleRelQueryParams {
    role_code: String,
    res_name: Option<String>,
    res_code: Option<String>,
    res_type: Option<String>,
}

pub async fn query_role_rel_page(request: BizRequest) -> BizHandlerResult {
    let state = request.get_state().unwrap();
    let db = &state.db_conn;
    let biz_content = request.get_biz_content();
    let params = PageRequest::<ResourceRoleRelQueryParams>::try_from(biz_content)?;

    // 获取查询条件
    let Some(query_params) = params.params else {
        return Err(BizError::InvalidParams(None).into());
    };

    let page_num = params.page_num as u32;
    let page_size = params.page_size as u32;
    let mut sql_builder = RawSqlQueryBuilder::new();

    sql_builder.push_bind("and x.role_code=", query_params.role_code.into());

    if let Some(res_type) = query_params.res_type {
        sql_builder.push_bind("and sr.res_type=", res_type.into());
    }

    if !query_params.res_name.is_blank() {
        sql_builder.push_bind(
            "and sr.res_name LIKE ",
            format!("{}%", query_params.res_name.unwrap()).into(),
        );
    }
    if !query_params.res_code.is_blank() {
        sql_builder.push_bind("and sr.res_code = ", query_params.res_code.unwrap().into());
    }

    let sql_and_vals = sql_builder.build(
        "
    SELECT sr.id,
        sr.delete_flag ,
        sr.res_code,
        sr.res_name,
        sr.res_description,
        sr.res_type ,
        sr.create_time , 
        sr.update_time
    FROM rbac_role_resource_rel x 
    INNER JOIN sys_resource sr on sr.res_code = x.res_code 
    ",
        Some("res_name ,id desc"),
    );

    let stmt = database::from_string_values_tuple(sql_and_vals);
    let selector = ResourceItem::find_by_statement(stmt);

    let pager = selector.paginate(db, page_size as u64);
    let page_list = pager.fetch_page((page_num - 1) as u64).await.unwrap();
    let total = pager.num_items().await.unwrap();

    let data = PageResult::new(page_num as u64, page_size as u64, total as u64, page_list);
    Ok(BizResponse::success(data.into()))
}
